package com.ikun.kunpicturebackend.manager.websocket;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ikun.kunpicturebackend.domain.User;
import com.ikun.kunpicturebackend.manager.websocket.disruptor.PictureEditEventProducer;
import com.ikun.kunpicturebackend.manager.websocket.model.PictureEditActionEnum;
import com.ikun.kunpicturebackend.manager.websocket.model.PictureEditMessageTypeEnum;
import com.ikun.kunpicturebackend.manager.websocket.model.PictureEditRequestMessage;
import com.ikun.kunpicturebackend.manager.websocket.model.PictureEditResponseMessage;
import com.ikun.kunpicturebackend.model.vo.UserVO;
import com.ikun.kunpicturebackend.service.UserService;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket处理器
 */
@Component
public class PictureEditHandler extends TextWebSocketHandler {

    @Resource
    private PictureEditEventProducer pictureEditEventProducer;

    // 每张图片的编辑状态，图片id -> 当前正在编辑的用户id
    private final Map<Long, Long> pictureEditingUsers = new ConcurrentHashMap<>();

    // 保存所有WebSocket连接的会话，图片id -> 会话集合
    private final Map<Long, Set<WebSocketSession>> pictureSessions = new ConcurrentHashMap<>();

    /**
     * 将消息广播给该图片对应的所有会话
     *
     * @param pictureId       图片id
     * @param responseMessage 响应消息
     * @param excludeSession  排除的会话
     */
    private void broadcastToPicture(Long pictureId, PictureEditResponseMessage responseMessage,
                                    WebSocketSession excludeSession) throws IOException {
        // 获取会话集合
        Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId);
        if (CollUtil.isEmpty(sessionSet))
            return;

        // 创建ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        // 配置序列化：将Long类型转换为String，解决精度丢失。
        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(module);

        // 将消息序列化为JSON字符串
        String message = objectMapper.writeValueAsString(responseMessage);
        TextMessage textMessage = new TextMessage(message);
        // 遍历会话集合
        for (WebSocketSession session : sessionSet) {
            // 排除的session不用发送
            if (session.equals(excludeSession)) {
                continue;
            }
            if (session.isOpen()) {
                // 发送消息
                session.sendMessage(textMessage);
            }
        }
    }

    /**
     * 全部广播
     *
     * @param pictureId
     * @param responseMessage
     * @throws IOException
     */
    private void broadcastToPicture(Long pictureId, PictureEditResponseMessage responseMessage) throws IOException {
        broadcastToPicture(pictureId, responseMessage, null);
    }

    /**
     * 连接建立成功后执行，保存当前会话到集合，并广播消息
     * @param session 当前会话
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 获取会话信息
        User user = (User) session.getAttributes().get("user");
        Long pictureId = (Long) session.getAttributes().get("pictureId");
        // 保存会话到集合中
        pictureSessions.putIfAbsent(pictureId, ConcurrentHashMap.newKeySet());
        pictureSessions.get(pictureId).add(session);

        // 构造响应
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        String message = String.format("%s加入编辑", user.getUserName());
        pictureEditResponseMessage.setMessage(message);
        pictureEditResponseMessage.setUserVO(BeanUtil.copyProperties(user, UserVO.class));
        // 广播给同一张图片的用户
        broadcastToPicture(pictureId, pictureEditResponseMessage);
    }

    /**
     * 连接关闭后执行
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // 从session获取信息
        Map<String, Object> attributes = session.getAttributes();
        Long pictureId = (Long) attributes.get("pictureId");
        User user = (User) attributes.get("user");
        // 移除当前用户的编辑状态
        handleExitEditMessage(null, session, user, pictureId);

        // 从集合中删除会话
        Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId);
        if (sessionSet != null) {
            sessionSet.remove(session);
            if (sessionSet.isEmpty()) {
                pictureSessions.remove(pictureId);
            }
        }

        // 构造响应消息
        PictureEditResponseMessage responseMessage = new PictureEditResponseMessage();
        responseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        String message = String.format("%s离开编辑", user.getUserName());
        responseMessage.setMessage(message);
        responseMessage.setUserVO(BeanUtil.copyProperties(user, UserVO.class));
        // 广播
        broadcastToPicture(pictureId, responseMessage);
    }

    /**
     * 处理消息
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 将消息解析为PictureEditRequestMessage
        PictureEditRequestMessage pictureEditRequestMessage = JSONUtil.toBean(message.getPayload(), PictureEditRequestMessage.class);

        // 从session中获取公共参数
        Map<String, Object> attributes = session.getAttributes();
        User user = (User) attributes.get("user");
        Long pictureId = (Long) attributes.get("pictureId");

        // 生产消息
        pictureEditEventProducer.publishEvent(pictureEditRequestMessage, session, user, pictureId);
    }

    /**
     * 处理 退出编辑消息
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     */
    public void handleExitEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws IOException {
        Long editingUserId = pictureEditingUsers.get(pictureId);
        if (editingUserId != null && editingUserId.equals(user.getId())) {
            // 移除当前用户的编辑状态
            pictureEditingUsers.remove(pictureId);
            // 构造响应
            PictureEditResponseMessage responseMessage = new PictureEditResponseMessage();
            responseMessage.setType(PictureEditMessageTypeEnum.EXIT_EDIT.getValue());
            String message = String.format("%s退出编辑图片", user.getUserName());
            responseMessage.setMessage(message);
            responseMessage.setUserVO(BeanUtil.copyProperties(user, UserVO.class));
            // 广播
            broadcastToPicture(pictureId, responseMessage);
        }
    }

    /**
     * 处理 编辑动作消息
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     */
    public void handleEditActionMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws IOException {
        // 获取信息
        // 正在编辑的用户id
        Long editingUserId = pictureEditingUsers.get(pictureId);
        // 编辑动作
        String editAction = pictureEditRequestMessage.getEditAction();
        PictureEditActionEnum actionEnum = PictureEditActionEnum.getEnumByValue(editAction);
        if (actionEnum == null) {
            return;
        }

        // 判断是否是正在编辑的用户
        if (editingUserId != null && editingUserId.equals(user.getId())) {
            // 构造响应
            PictureEditResponseMessage responseMessage = new PictureEditResponseMessage();
            responseMessage.setEditAction(editAction);
            responseMessage.setType(PictureEditMessageTypeEnum.EDIT_ACTION.getValue());
            String message = String.format("%s执行%s", user.getUserName(), actionEnum.getText());
            responseMessage.setMessage(message);
            responseMessage.setUserVO(BeanUtil.copyProperties(user, UserVO.class));
            // 发送给其他客户端
            broadcastToPicture(pictureId, responseMessage, session);
        }
    }

    /**
     * 处理 进入编辑消息
     * @param pictureEditRequestMessage
     * @param session
     * @param user
     * @param pictureId
     */
    public void handleEnterEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws IOException {
        // 没有用户在编辑此图片才能进入，互斥
        if (!pictureEditingUsers.containsKey(pictureId)) {
            // 设置当前用户为编辑用户
            pictureEditingUsers.put(pictureId, user.getId());
            // 构造响应消息
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ENTER_EDIT.getValue());
            String message = String.format("%s开始编辑图片", user.getUserName());
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setUserVO(BeanUtil.copyProperties(user, UserVO.class));
            // 广播给其他连接
            broadcastToPicture(pictureId, pictureEditResponseMessage);
        }
    }

    


}
